package com.mycompany.myapp.service.impl;

import com.mycompany.myapp.domain.Authority;
import com.mycompany.myapp.repository.AuthorityRepository;
import com.mycompany.myapp.security.AuthoritiesConstants;
import com.mycompany.myapp.service.CartService;
import com.mycompany.myapp.service.SUserService;
import com.mycompany.myapp.domain.SUser;
import com.mycompany.myapp.repository.SUserRepository;
import com.mycompany.myapp.service.dto.SUserDTO;
import com.mycompany.myapp.service.dto.SUserInfoDTO;
import com.mycompany.myapp.service.mapper.SUserMapper;
import com.mycompany.myapp.web.rest.errors.PhoneAlreadyUsedException;
import com.mycompany.myapp.web.rest.utils.*;
import com.mycompany.myapp.web.rest.vm.QueryVM;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.cache.CacheManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Service Implementation for managing {@link SUser}.
 */
@Service
@Transactional
public class SUserServiceImpl implements SUserService {

    private final Logger log = LoggerFactory.getLogger(SUserServiceImpl.class);

    private final SUserRepository sUserRepository;

    private final SUserMapper sUserMapper;

    private final PasswordEncoder passwordEncoder;

    private final AuthorityRepository authorityRepository;

    private final CacheManager cacheManager;

    private final CartService cartService;


    public SUserServiceImpl(SUserRepository sUserRepository, SUserMapper sUserMapper,
                            PasswordEncoder passwordEncoder, AuthorityRepository authorityRepository,
                            CacheManager cacheManager, CartService cartService) {
        this.sUserRepository = sUserRepository;
        this.sUserMapper = sUserMapper;
        this.passwordEncoder = passwordEncoder;
        this.authorityRepository = authorityRepository;
        this.cacheManager = cacheManager;
        this.cartService = cartService;
    }

    /**
     * Save a sUser.
     *
     * @param sUserDTO the entity to save.
     * @return the persisted entity.
     */
    @Override
    public SUserDTO save(SUserDTO sUserDTO) {
        log.debug("Request to save SUser : {}", sUserDTO);
        SUser sUser = sUserMapper.toEntity(sUserDTO);
        sUser = sUserRepository.save(sUser);
        return sUserMapper.toDto(sUser);
    }

    /**
     * Get all the sUsers.
     *
     * @return the list of entities.
     */
    @Override
    @Transactional(readOnly = true)
    public List<SUserDTO> findAll() {
        log.debug("Request to get all SUsers");
        return sUserRepository.findAll().stream()
            .map(sUserMapper::toDto)
            .collect(Collectors.toCollection(LinkedList::new));
    }


    /**
     * Get one sUser by id.
     *
     * @param id the id of the entity.
     * @return the entity.
     */
    @Override
    @Transactional(readOnly = true)
    public Optional<SUserDTO> findOne(Long id) {
        log.debug("Request to get SUser : {}", id);
        return sUserRepository.findById(id)
            .map(sUserMapper::toDto);
    }

    /**
     * Delete the sUser by id.
     *
     * @param id the id of the entity.
     */
    @Override
    public void delete(Long id) {
        log.debug("Request to delete SUser : {}", id);
        sUserRepository.deleteById(id);
    }

    @Override
    public ResultObj registerUser(SUserDTO sUserDTO, String password) {
/*        sUserRepository.findOneByPhone(sUserDTO.getPhone()).ifPresent(existingUser -> {
            boolean removed = removeNonActivatedUser(existingUser);
            if (!removed) {
                //throw new PhoneAlreadyUsedException();
            }
        });*/

        if(sUserRepository.findOneByPhone(sUserDTO.getPhone()).isPresent()){
           SUser existingUser = sUserRepository.findOneByPhone(sUserDTO.getPhone()).get();
            boolean removed = removeNonActivatedUser(existingUser);
            if (!removed) {
                return ResultObj.back(201,null);
            }
        }

        SUser newUser = new SUser();
        String encryptedPassword = passwordEncoder.encode(password);
        newUser.setUsername(sUserDTO.getUsername());
        // new user gets initially a generated password
        newUser.setPassword(encryptedPassword);
        newUser.setEmail(sUserDTO.getEmail().toLowerCase());
        newUser.setAvatar(sUserDTO.getAvatar());
        newUser.setPhone(sUserDTO.getPhone());
        // new user is active
        newUser.setIsActive(true);
        newUser.setCreateTime(DateUtil.getZoneDateTime());
        newUser.setUpdateTime(DateUtil.getZoneDateTime());
        Set<Authority> authorities = new HashSet<>();
        authorityRepository.findById(AuthoritiesConstants.USER).ifPresent(authorities::add);
        newUser.setAuthorities(authorities);
        sUserRepository.save(newUser);
        cartService.createCart(sUserRepository.findOneByEmail(newUser.getEmail()).getId());
        this.clearUserCaches(newUser);
        log.debug("Created Information for User: {}", newUser);
        return ResultObj.back(200,"添加成功",newUser);
    }


    @Transactional(readOnly = true)
    public List<SUserInfoDTO> findAllSUser() {
        List<SUser> sUserList = sUserRepository.findAll();
        List<SUserInfoDTO> sUserDTOList = new ArrayList<>();
        for (SUser sUser : sUserList) {
            SUserInfoDTO sUserInfoDTO = new SUserInfoDTO(sUser);
            sUserDTOList.add(sUserInfoDTO);
        }
        return sUserDTOList;
    }

    @Transactional(readOnly = true)
    public Optional<SUser> findOneByPhone(String phone) {
        Optional<SUser> sUser = sUserRepository.findOneWithAuthoritiesByPhone(phone);
        return sUser;
    }

    private boolean removeNonActivatedUser(SUser existingUser) {
        if (existingUser.getIsActive()) {
            return false;
        }
        sUserRepository.delete(existingUser);
        sUserRepository.flush();
        this.clearUserCaches(existingUser);
        return true;
    }

    private void clearUserCaches(SUser sUser) {
        /*Objects.requireNonNull(cacheManager.getCache(SUserRepository.USERS_BY_PHONE_CACHE)).evict(sUser.getPhone());
        Objects.requireNonNull(cacheManager.getCache(SUserRepository.USERS_BY_EMAIL_CACHE)).evict(sUser.getEmail());*/
    }

    /**
     * Update userInfo by sUser
     */
    @Override
    public ResultObj updateSUser(SUserDTO sUserDTO) {
        if (!TypeUtils.isEmpty(sUserDTO)) {
            if (!TypeUtils.isEmpty(sUserDTO.getId())) {
                save(sUserDTO);
                return ResultObj.backInfo(true, 200, "修改成功", null);
            }
        }
        return ResultObj.backInfo(false, 200, "修改失败", null);
    }

    public ResultObj sendEmail(String Email, HttpSession session) {
        SUser sUser = sUserRepository.findOneByEmail(Email);
        String verifyCode;
        String mailContent;
        if (!TypeUtils.isEmpty(Email)) {
            if (!TypeUtils.isEmpty(sUser)) {
                verifyCode = VerifyCodeUtil.getEmailVerifyCode();
                session.setAttribute("emailVerifyCode", verifyCode);
                session.setAttribute("userEmail", Email);
                mailContent = "验证码为：" + verifyCode + "<br/>(此为系统邮件，请勿直接回复)";
                SendMailUtil.sendMail(Email, mailContent);
                return ResultObj.backInfo(false, 200, "发送成功！", null);
            } else {
                return ResultObj.backInfo(false, 200, "该邮箱未绑定账号", null);
            }
        } else {
            return ResultObj.backInfo(false, 200, "请输入邮箱", null);
        }
    }

    public ResultObj alterPassword(String verifycode, String password, HttpSession session) {
        SUser sUser = sUserRepository.findOneByEmail((String) session.getAttribute("userEmail"));
        String emailverifycode = (String) session.getAttribute("emailVerifyCode");
        String encryptedPassword = passwordEncoder.encode(password);
        if (!TypeUtils.isEmpty(sUser)) {
            if (!TypeUtils.isEmpty(emailverifycode) && emailverifycode.equals(verifycode)) {
                sUser.setPassword(encryptedPassword);
                sUserRepository.save(sUser);
                session.removeAttribute("emailVerifyCode");
                return ResultObj.backInfo(false, 200, "修改成功！", null);
            } else {
                return ResultObj.backInfo(false, 200, "验证码错误", null);
            }
        } else {
            return ResultObj.backInfo(false, 500, "修改失败！", null);
        }
    }

    public ResultObj checkEmail(String email) {
        if (!TypeUtils.isEmpty(email)) {
            if (!TypeUtils.isEmpty(sUserRepository.findOneByEmail(email))) {
                return ResultObj.backInfo(false, 200, "该邮箱已被注册！", null);
            } else {
                return ResultObj.backInfo(true, 200, "该邮箱可以使用！", null);
            }
        }
        return ResultObj.backInfo(false, 200, "邮箱为空！", null);
    }

    public ResultObj findSUserByUsername(String username) {
        SUser sUser = sUserRepository.findByUsername(username);
        if (!TypeUtils.isEmpty(sUser)) {
            return ResultObj.backInfo(true, 200, "查找成功！", sUser);
        }
        return ResultObj.backInfo(false, 200, "无该用户信息！", null);
    }


    public ResultObj findSUser(String username, String password) {
        SUser sUser = sUserRepository.findByUsernameAndPassword(username, password);
        if (!TypeUtils.isEmpty(sUser)) {
            return ResultObj.backInfo(true, 200, "登录成功！", sUser);
        }
        return ResultObj.backInfo(false, 200, "用户名或密码错误！", null);
    }

    @Override
    public ResultObj changePassword(Long userId, String password) {
        String encryptedPassword = passwordEncoder.encode(password);
        int index = 0;
        index = sUserRepository.changePasswordById(encryptedPassword, userId);
        if (index > 0) {
            return ResultObj.backInfo(true, 200, "修改成功！", null);
        }
        return ResultObj.backInfo(true, 201, "修改失败！", null);
    }

    @Override
    public Page<SUser> findByName(int pageNum, int pageSize, String username) {
        Pageable pageable = PageRequest.of(pageNum-1, pageSize);
        Page<SUser> sUserList = sUserRepository.findSUsersByUsernameLike("%" + username + "%", pageable);
        return sUserList;
    }

    @Override
    public ResultObj updateState(long userId, boolean isActive) {
        if (sUserRepository.updateState(userId, isActive) > 0)
            return ResultObj.back(200, sUserRepository.findById(userId).get());
        return ResultObj.back(201, null);
    }

    @Override
    public SUserInfoDTO findById(Long id) {
        SUser sUser = sUserRepository.findById(id).get();
        if (!TypeUtils.isEmpty(sUser)) {
            SUserInfoDTO sUserInfoDTO = new SUserInfoDTO(sUser);
            return sUserInfoDTO;
        }
        return null;
    }

    @Override
    public ResultObj updateSUserM(SUserDTO sUser) {
        SUser user = sUserRepository.findById(sUser.getId()).get();
        user.setEmail(sUser.getEmail());
        user.setPhone(sUser.getPhone());

        if (!TypeUtils.isEmpty(user)) {
            return ResultObj.back(200, sUserRepository.save(user));
        }
        return ResultObj.back(201, null);
    }

    @Override
    public ResultObj updateRole(Long id, String role) {
        if (sUserRepository.findById(id).isPresent()){
            sUserRepository.findById(id).ifPresent(sUser -> {
                Set<Authority> authorities = new HashSet<>();
                authorityRepository.findById(role).ifPresent(authorities::add);
                sUser.setAuthorities(authorities);
                sUserRepository.save(sUser);
            });
            return ResultObj.back(200, null);
        }
        return ResultObj.back(201, null);
    }

}
